Guia de Uso dos Repositórios
Visão Geral
Este guia detalha como utilizar o padrão de Repositório para acesso a dados em nosso projeto. Este padrão abstrai a fonte de dados — seja o banco de dados local (RxDB) ou uma API remota — centralizando e organizando toda a lógica de acesso.
Adoção deste padrão nos ajuda a manter o código limpo, testável e fácil de manter. Além disso, otimiza a performance e a experiência do usuário através do cache local e das capacidades reativas fornecidas pelo RxDB.
Arquitetura
O fluxo de dados segue um caminho claro, projetado para garantir consistência e performance. Um componente React nunca acessa a fonte de dados diretamente. Em vez disso, ele utiliza hooks que orquestram o acesso através da camada de repositório.
Como Usar
1. Acessando um Repositório
Para acessar os repositórios em seus componentes React, utilize o hook useRepository.
import { useRepository } from "src/context/rxdb";
function MyComponent() {
// O nome da propriedade corresponde ao repositório desejado (ex: productsRepo)
const { productsRepo } = useRepository();
// Agora você pode usar `productsRepo` para acessar os dados.
// ...
}
O productsRepo pode ser null durante a renderização inicial, pois o banco de dados é inicializado de forma assíncrona. A seção seguinte explica como lidar com isso.
2. Buscando Dados com React Query
A maneira padrão para buscar dados de um repositório é com a biblioteca @tanstack/react-query. Ela simplifica o gerenciamento de estado assíncrono, cache e re-fetching.
O hook useQuery é a principal ferramenta. A opção enabled é crucial para garantir que a consulta só seja executada quando a instância do repositório estiver disponível.
Exemplo: Buscando todos os produtos
import { useRepository } from "src/context/rxdb";
import { useQuery } from "@tanstack/react-query";
import type { Product } from "src/types/product/product.types";
function ProductList() {
// 1. Obtenha a instância do repositório.
const { productsRepo } = useRepository();
// 2. Use `useQuery` para buscar os dados.
const { data, isLoading, error } = useQuery({
// `queryKey` é um array que identifica unicamente esta consulta.
queryKey: ["products", "all"],
// `queryFn` é a função que busca os dados.
queryFn: async () => {
// O `!` é seguro aqui por causa da verificação `enabled`.
return productsRepo!.fetchAll();
},
// `enabled` é a chave! A consulta fica em espera até que
// `productsRepo` não seja mais nulo.
enabled: !!productsRepo,
});
const products = data?.data || [];
if (isLoading) {
return <div>Carregando produtos...</div>;
}
if (error) {
return <div>Ocorreu um erro: {error.message}</div>;
}
return (
<ul>
{products.map((product) => (
<li key={product.CODPROD}>{product.DESCRPROD}</li>
))}
</ul>
);
}
Exemplos Avançados de Consulta
A queryKey do useQuery é fundamental. Ela deve incluir todos os parâmetros que podem invalidar a consulta, garantindo que o React Query busque novos dados quando os filtros mudarem.
Filtrando Produtos por Marca
import { useRepository } from "src/context/rxdb";
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
function FilteredProductList() {
const { productsRepo } = useRepository();
const [selectedBrand, setSelectedBrand] = useState("Apple");
const { data, isLoading } = useQuery({
// Adicione `selectedBrand` à queryKey.
// Se `selectedBrand` mudar, o React Query fará uma nova busca.
queryKey: ["products", "byBrand", selectedBrand],
queryFn: () => productsRepo!.fetchByBrand({ marca: selectedBrand }),
// A consulta depende do repositório estar pronto.
enabled: !!productsRepo,
});
// ...
}
O Poder do Mango Query Syntax
O RxDB utiliza uma sintaxe de consulta inspirada no MongoDB e popularizada pelo CouchDB, chamada Mango Query. Ela permite construir consultas complexas usando um objeto JSON.
Link Externo: Para uma referência completa dos operadores (
$eq,$gt,$in,$regex, etc.), consulte a Documentação oficial do Mango Query.
Internamente, os métodos do nosso repositório (como fetchByBrand) constroem um selector do Mango Query.
Por exemplo, productsRepo.fetchByBrand({ marca: 'Apple' }) cria um seletor assim:
{
"selector": {
"MARCA": {
"$eq": "Apple"
}
}
}
Você também pode encontrar seletores mais complexos no código, como em fetchWithStock, que busca produtos com estoque disponível maior ou igual a um valor mínimo:
// Exemplo de dentro do repositório
const selector = {
ESTOQUEPADRAO: {
$elemMatch: {
// Encontra um elemento no array ESTOQUEPADRAO que corresponda...
DISPONIVEL: { $gte: 1 }, // ...a ter o campo DISPONIVEL maior ou igual a 1.
},
},
};
Repositórios Disponíveis
Repositório de Produtos (productsRepo)
fetchAll(params?: FetchPaginationParams): Busca todos os produtos com paginação.fetchByBrand(params: FetchByBrandParams): Busca produtos por uma ou mais marcas.fetchByManufacturer(params: FetchByManufacturerParams): Busca produtos por um ou mais fabricantes.fetchByGroup(params: FetchByGroupParams): Busca produtos por um ou mais grupos.search(params: SearchParams): Realiza uma busca textual nos produtos.findByCodProd(params: FindByCodProdParams): Encontra um produto pelo seu código.findByBarcode(params: FindByBarcodeParams): Encontra um produto pelo código de barras.syncFromAPI(params: SyncFromAPIParams): Sincroniza um lote de produtos da API para o banco local.clear(): Apaga todos os documentos da coleção.
Consulte a interface IProductRepository para a lista completa de métodos.
Limpeza de Dados no Logout
Importante: Não é necessário se preocupar em limpar os dados ao fazer logout. O sistema já está configurado para apagar todas as coleções do banco de dados local automaticamente quando o usuário sai da aplicação.
Criando Novos Repositórios
Para adicionar um repositório para uma nova coleção (ex: clients):
-
Crie a interface do repositório:
src/db/repositories/clients.repository.tsexport interface IClientRepository {
findById(id: number): Promise<Client | null>;
// ...outros métodos
} -
Crie a implementação RxDB:
src/db/rxdb/repositories/clients.repository.tsimport type { IClientRepository } from "../../repositories/clients.repository";
export class ClientsRepositoryRxDB implements IClientRepository {
constructor(private db: DBSchema) {}
// ...implementação dos métodos usando this.db.clients
} -
Atualize o
provider.tsx(src/app/provider.tsx):- No
dbStart, instancieClientsRepositoryRxDB. - Registre o repositório usando
register("clients", ...). - Configure o estado correspondente para disponibilizá-lo no
RepositoryProvider. - Passe a nova instância para o
RepositoryProvider.
export default function Provider({
children,
}: {
children: React.ReactNode;
}) {
const [productsRepo, setProductsRepo] =
React.useState<IProductRepository | null>(null);
const [clientsRepo, setClientsRepo] =
React.useState<IClientRepository | null>(null);
const dbStart = async () => {
const db = await getDB();
// Registro dos repositórios
register("products", new ProductsRepositoryRxDB(db));
register("clients", new ClientsRepositoryRxDB(db));
// Estado interno
setProductsRepo(new ProductsRepositoryRxDB(db));
setClientsRepo(new ClientsRepositoryRxDB(db));
};
React.useEffect(() => {
dbStart();
}, []);
return (
<RepositoryProvider
repositories={{
productsRepo,
clientsRepo,
}}
>
{children}
</RepositoryProvider>
);
} - No
-
Atualize o Contexto (
src/context/rxdb/index.tsx):- Adicione o novo repositório à interface
RepositoryContextValue. - Atualize os tipos do
RepositoryProviderpara incluir o novo repositório (clientsRepo).
- Adicione o novo repositório à interface
Links Úteis
- Documentação Oficial do RxDB: O melhor lugar para começar a aprender sobre o RxDB, suas APIs e seus recursos.
- Sintaxe de Seletores Mango: Referência completa para a construção de consultas.
- React Query Docs: Guia essencial para gerenciar estado assíncrono em React.